1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
"""
5/11/2023 by Zehao Li

This Flask application controls and monitors a moving robot car through a web server running on a Raspberry Pi. The
application provides an interface to stream video, control the movement of the object, change operational modes,
and interact with different buttons.

The application uses multiprocessing to handle concurrent operations, and RPi.GPIO to interact with the GPIO pins of
the Raspberry Pi. The script provides several routes (endpoints) that handle different HTTP requests to control and
monitor the moving object. 

The 'rolling_control' module is assumed to provide the means to control the movement of the object. Communication
between processes is done through a multiprocessing Queue and shared data structures are protected by locks to
prevent race conditions.

This code should be run on a Raspberry Pi with a connected camera and appropriate hardware to control the movement of
the object. """
from flask import Flask, render_template, Response, request
from multiprocessing import Queue, Manager
import RPi.GPIO as GPIO
import json
import time
from rolling_control import Rolling_Control

app = Flask(__name__)
q = Queue()
mode = 1
rolling = Rolling_Control()


# This function is a generator function which constantly yields frames from the queue
def gen_frames():
    while True:
        if not q.empty():  # Check if the queue is not empty
            frame = q.get()  # Get the next frame from the queue
            yield (b'--frame\r\n'
                   b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
        time.sleep(0.02)


# It returns the frames that are being generated by the gen_frames function.
@app.route('/video_feed')
def video_feed():
    return Response(gen_frames(),
                    mimetype='multipart/x-mixed-replace; boundary=frame')


# It returns the index.html template.
@app.route('/index.html')
def index():
    return render_template('index.html')


# It updates shared_data based on the button_id that is posted.
@app.route('/button_click', methods=['POST'])
def button_click():
    global shared_data, lock
    with lock:
        button_id = request.json['button_id']
        print(f'Button "{button_id}" clicked')
        if button_id == 'cancel-tracking':
            shared_data['coordinates'] = [None, None]
            shared_data['tracking_initialized'] = False  # reset the flag
        elif button_id == 'pause-tracking':
            shared_data['tracking_paused'] = True  # pause the tracking
        elif button_id == 'resume-tracking':
            shared_data['tracking_paused'] = False  # resume the tracking
        shared_data['functional'] = button_id
        return 'Button click handled'


# It changes the mode based on the mode that is posted.
@app.route('/change_mode', methods=['POST'])
def change_mode():
    global mode, shared_data, lock
    with lock:
        max_mode_num = 3
        if shared_data['mode_idx'] + 1 >= max_mode_num:
            shared_data['mode_idx'] = 0
        else:
            shared_data['mode_idx'] += 1
        shared_data['mode_change_signal'] = True

        mode = request.json['mode']
        if mode == 2 and shared_data['23']:
            shared_data['coordinates'] = [None, None]

        print(f'Mode changed to {mode}')
        return 'Mode change handled'


# It returns the current mode and other related status variables.
@app.route('/get_current_mode')
def get_current_mode():
    global shared_data, mode, lock
    with lock:
        mode = shared_data['mode_idx'] + 1
        if shared_data.get('tracking_status'):
            tracking_status = shared_data['tracking_status']
        else:
            tracking_status = False
        button22 = shared_data['22']
        button23 = shared_data['23']
        button27 = shared_data['27']
        shared_data['22'] = False
        shared_data['23'] = False
        shared_data['27'] = False
        return json.dumps({
            'mode': shared_data['mode_idx'] + 1,
            'tracking_status': tracking_status,
            'button22': button22,
            'button23': button23,
            'button27': button27
        })


# It controls the rolling of the robot based on the direction that is posted.
@app.route('/control_rolling', methods=['POST'])
def control_rolling():
    rolling_direction = request.json['rolling_direction']
    if rolling_direction == "forward":
        rolling.rolling_forward()
    elif rolling_direction == "backward":
        rolling.rolling_backward()
    elif rolling_direction == "right":
        rolling.rolling_right()
    elif rolling_direction == "left":
        rolling.rolling_left()
    print(f'Car is rolling {rolling_direction}')
    return 'Rolling control handled'


# It stops the rolling of the robot.
@app.route('/exit_rolling', methods=['POST'])
def exit_rolling():
    rolling.rolling_exit()
    print(f'Car stops rolling')
    return 'Rolling control handled'


# It sets the coordinates in the shared_data.
@app.route('/set_coordinates', methods=['POST'])
def set_coordinates():
    global shared_data, lock
    with lock:
        coordinates = request.json['coordinates']
        print(f'Coordinates received: {coordinates}')
        shared_data['coordinates'] = coordinates
        shared_data['tracking_initialized'] = False  # reset the flag
        return 'Coordinates set'


# It sets the manual coordinates in the shared_data.
@app.route('/set_manual_coordinates', methods=['POST'])
def set_manual_coordinates():
    global shared_data, lock
    with lock:
        coordinates = request.json['coordinates']
        print(f'Manual coordinates received: {coordinates}')
        shared_data['manual_coor'] = coordinates
        return 'Manual coordinates set'


# Main entry point for the script. Starts the Flask application.
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)